/*

Copyright (C) 2007 Thomas Geiger (tom.geiger@gmail.com)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

#include "GlobusXIOClient.h"

namespace globus_xio {

GlobusXIOClient::GlobusXIOClient()
{
	globus_module_activate(GLOBUS_XIO_MODULE);
	globus_xio_stack_init(&stack, NULL);
	globus_xio_attr_init(&attr);	
}

GlobusXIOClient::~GlobusXIOClient()
{
    // destroy attr
    globus_xio_attr_destroy(attr);
	globus_module_deactivate(GLOBUS_XIO_MODULE);
}

void GlobusXIOClient::receive(iostream& input)
{	
	char buffer[BUF_SIZE];
	globus_size_t nrOfBytes;
		
	globus_result_t res;
	
	res = globus_xio_handle_create(&handle, stack);
	test_error(res, __LINE__);
    res = globus_xio_open(handle, const_cast<char*>( url->getURL().c_str() ), NULL);
    test_error(res, __LINE__);
    
    do
    {
        res = globus_xio_read(handle, (globus_byte_t*) buffer, 
        					  sizeof(buffer) - 1, 1, &nrOfBytes, NULL);
        if(nrOfBytes > 0)
        {
            buffer[nrOfBytes] = '\0';
            input << buffer;
        }
    } while (res == GLOBUS_SUCCESS);
    
    globus_xio_close(handle, NULL);
}


void GlobusXIOClient::send(iostream& output) 
{	
	globus_size_t nrOfBytes = 0;	
	globus_result_t res;

	// determine size of stream
	output.seekg(0, ios::end);
  	int size = output.tellg();
  	output.seekg(0, ios::beg);

    /// TODO: split buffer to a smaller size
	char* buffer = new char[size];
	output.read(buffer, size);

	/// XXX: try to enable http post
	if (url->getScheme() == "http" || url->getScheme() == "https") {
		cerr << "setting post" << endl;
	 	
		res = globus_xio_handle_create(&handle, stack);
		test_error(res, __LINE__);
	    
		res = globus_xio_attr_cntl(attr, driver, GLOBUS_XIO_HTTP_ATTR_SET_REQUEST_METHOD, "POST");
	 	test_error(res, __LINE__);
	 	
	 	cerr << "post" << endl;
	 	
	 	// set http 1.1
	 	res = globus_xio_attr_cntl(
                attr,
                driver,
                GLOBUS_XIO_HTTP_ATTR_SET_REQUEST_HTTP_VERSION,
                GLOBUS_XIO_HTTP_VERSION_1_1);
	 	test_error(res, __LINE__);
		
	 	cerr << "http1.1" << endl;
	 	
	    globus_xio_http_header_t headers[2];
	    int header_cnt = 0;
	    char content_length_buffer[64];
	    
	    headers[header_cnt].name = "Transfer-Encoding";
        headers[header_cnt].value = "identity";
        header_cnt++;
	    
        sprintf(content_length_buffer, "%ld", size);
	cerr << "size: " << size << endl;
	cerr << content_length_buffer << endl;

        headers[header_cnt].name = "Content-Length";
        headers[header_cnt].value = &content_length_buffer[0];
        header_cnt++;
        
	 	
	 	// set header
	 	for (int i = 0; i < header_cnt; i++)
	    {
	    	cout << "setting header: " << headers[i].name << endl;
	         res = globus_xio_attr_cntl(
	                 attr,
	                 driver,
	                 GLOBUS_XIO_HTTP_ATTR_SET_REQUEST_HEADER,
	                 headers[i].name,
	                 headers[i].value);

	         if (res != GLOBUS_SUCCESS)
	         {
	        	 cout << "error when setting headers" << endl;
	             return;
	         }
	    }
	 	 
		res = globus_xio_open(handle, const_cast<char*>( url->getURL().c_str() ), attr);
	    test_error(res, __LINE__);
		
	    cerr << "open" << endl;
	    
	 	globus_size_t message_size = size;
    	globus_size_t buffer_size = 0;
	 	        
	 	//char *                              ptr = buffer;
    	globus_size_t                       left = size;
    	globus_size_t                       to_write;
    	globus_size_t                       nbytes;
    	globus_result_t                     result = GLOBUS_SUCCESS;
	 		 	
	 	if (buffer_size == 0)
	    {
	        buffer_size = 1024;
	    }
	    cout << "write: " << buffer << endl;
	    while ((left > 0) && (result == GLOBUS_SUCCESS))
	    {
	        to_write = (left > buffer_size) ? buffer_size : left;
	        result = globus_xio_write(
	                handle,
	                (globus_byte_t*) buffer,	               
	                to_write,
			to_write,
	                &nbytes,
	                NULL);
	
	        
	        if (result == GLOBUS_SUCCESS)
	        {
	            if (nbytes != to_write)
	            {
	                fprintf(stderr, "Didn't write all I expected.\n");
	                //result = GlobusXIOErrorEOF();
	            }
	            left -= nbytes;
	            buffer += nbytes;
	        }
	        else
	        {
	            fprintf(stderr, "Error writing data: %s\n",
	                    globus_object_printable_to_string(globus_error_peek(result)));
	        }
	    }
	
	    cerr << "set end" << endl;
	    test_error(res, __LINE__);
	    
	    res = globus_xio_handle_cntl(
	            handle,
	            driver,
	            GLOBUS_XIO_HTTP_HANDLE_SET_END_OF_ENTITY);

	    cerr << "close" << endl;
	    
		// close handle    
	    res = globus_xio_close(handle, attr);
	    test_error(res, __LINE__);
	    
	} else {

		res = globus_xio_handle_create(&handle, stack);
		test_error(res, __LINE__);
	    res = globus_xio_open(handle, const_cast<char*>( url->getURL().c_str() ), NULL);
	    test_error(res, __LINE__);
	
		// send it out there
	    res = globus_xio_write(handle,
	                     (globus_byte_t*) buffer,
				         size, 
				         1, 
				         &nrOfBytes, 
				         NULL);
		
		test_error(res, __LINE__);
		
		// close handle    
	    globus_xio_close(handle, NULL);
        
	}
    cout << "1" << endl;
    delete[] buffer;
    cout << "2" << endl;
    return;
}

void GlobusXIOClient::setUrl(GlobusURL* newUrl) 
{
	url = newUrl;	
	initDriver(url);
}
		
GlobusURL* GlobusXIOClient::getUrl() 
{
	return url;
}	

void GlobusXIOClient::initDriver(GlobusURL* url) 
{			
	globus_result_t res;
	
	string schema = url->getScheme();
	
	if (schema == "file") {
		
    	res = globus_xio_driver_load("file", &driver);
	    res = globus_xio_stack_push_driver(stack, driver);
	    test_error(res, __LINE__);
	    
	} else if (schema == "http") {
		
		globus_xio_driver_t tcp_driver;
	    //globus_xio_driver_t http_driver;
	    
	    res = globus_xio_driver_load("tcp", &tcp_driver);
		res = globus_xio_stack_push_driver(stack, tcp_driver);
		test_error(res, __LINE__);
		res = globus_xio_driver_load("http", &driver);
		res = globus_xio_stack_push_driver(stack, driver);
		test_error(res, __LINE__);

	} else if (schema == "https") {

		globus_xio_driver_t tcp_driver;
	    globus_xio_driver_t gsi_driver;
	    //globus_xio_driver_t http_driver;
	    
	    res = globus_xio_driver_load("tcp", &tcp_driver);
		res = globus_xio_stack_push_driver(stack, tcp_driver);
		test_error(res, __LINE__);
	    res = globus_xio_driver_load("gsi", &gsi_driver);
		res = globus_xio_stack_push_driver(stack, gsi_driver);
		test_error(res, __LINE__);
		res = globus_xio_driver_load("http", &driver);
		res = globus_xio_stack_push_driver(stack, driver);
		test_error(res, __LINE__);
		
	} else if (schema == "ftp" || schema == "gsiftp") {
		
		res = globus_xio_driver_load("gridftp", &driver);
	    res = globus_xio_stack_push_driver(stack, driver);
	    test_error(res, __LINE__);
	    	    	
	} else {
		cerr << "unknown schema: " << schema << endl;	
	}
}

void GlobusXIOClient::test_error(globus_result_t res, int line)
{
    if(res == GLOBUS_SUCCESS)
    {
        return;
    }

	cerr << "Code line: " << line << ", ";
	cerr << "ERROR: " << globus_error_print_friendly( globus_error_peek(res) ) << endl;

    globus_assert(0);
}

}
